package org.wx.ormtest.step04.helper;

import javassist.*;
import lombok.extern.slf4j.Slf4j;
import org.wx.ormtest.step04.anno.Column;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 生成助手工厂
 * @author: wangxun
 * @createDate: 2021/3/2
 * @version: 1.0
 */
@Slf4j
public class EntityHelperFactory {
    /**
     * 类模型和对应的映射助手类的字典
     */
    private static final Map<Class<?>,AbstractEntityHelper> _entityHelperMap = new HashMap<>();

    private EntityHelperFactory(){}

    public static AbstractEntityHelper getEntityHelper(Class<?> entityClazz)throws Exception{
        if(null == entityClazz){
            return null;
        }
        // 在字典中取出
        AbstractEntityHelper entityHelper = _entityHelperMap.get(entityClazz);
        if(null != entityHelper){
            return entityHelper;
        }
        // 使用javassist 生成助手类的字节码文件
        // 获取默认池子
        ClassPool pool = ClassPool.getDefault();
        // 添加类地址
        ClassPath classPath = pool.appendSystemPath();


        // 添加 import java.sql.Result
        // 添加 import xxx.xxx.xxEntity
        pool.importPackage(ResultSet.class.getName());
        pool.importPackage(entityClazz.getName());

        CtClass abstractEntityHelperClazz = pool.getCtClass(AbstractEntityHelper.class.getName());

        final String helperClassName = entityClazz.getName()+"Helper";
        // 生成代码 public class UserHelper extends AbstractEntityHelper { ...
        // CtClass 代表一个类
        CtClass helperClass = pool.makeClass(helperClassName, abstractEntityHelperClazz);
        // 无参构造器
        // public UserEntity_Helper() {}
        CtConstructor constructor = new CtConstructor(new CtClass[0], helperClass);
        constructor.setBody("{}");
        // 添加构造器
        helperClass.addConstructor(constructor);

        //开启构造方法
        final StringBuffer sb = new StringBuffer();
        // 添加一个函数, 也就是实现抽象类中的 create 函数
        sb.append("public Object create(java.sql.ResultSet rs) throws Exception {\n");
        // 生成以下代码:
        // UserEntity obj = new UserEntity();
        sb.append(entityClazz.getName())
                .append(" obj = new ")
                .append(entityClazz.getName())
                .append("();\n");

        // 通过反射方式获取类的字段数组,
        // 并生成代码
        ///////////////////////////////////////////////////////////////////////
        //
        // 获取类的字段数组并生成代码
        Field[] fArr = entityClazz.getDeclaredFields();

        for (Field f : fArr) {
            f.setAccessible(true);
            // 获取字段上注解
            Column annoColumn = f.getAnnotation(Column.class);

            if (annoColumn == null) {
                // 如果注解为空,
                // 则直接跳过...
                continue;
            }

            // 获取列名称
            String colName = annoColumn.value();
            String filedName = f.getName();
            String firstStr = filedName.substring(0,1).toUpperCase();
            String endStr = f.getName().substring(1, filedName.length());


            if (f.getType() == Integer.TYPE) {
                // 生成如下代码:
                // obj._userId = rs.getInt("user_id");
                sb.append("obj.")
                        .append("set")
                        .append(firstStr)
                        .append(endStr)
                        .append("(")
                        .append(" rs.getInt(\"")
                        .append(colName)
                        .append("\"));\n");
            } else if (f.getType().equals(String.class)) {
                // 生成如下代码:
                // obj._userName = rs.getString("user_name");
                sb.append("obj.")
                        .append("set")
                        .append(firstStr)
                        .append(endStr)
                        .append("(")
                        .append(" rs.getString(\"")
                        .append(colName)
                        .append("\"));\n");
            } else {
                // 不支持的类型...
                // 如果需要支持 float、long、boolean 等类型,
                // 接着往下写就可以了
            }
        }

        sb.append("return obj;\n");
        sb.append("}");

        // 创建解析方法
        CtMethod cm = CtNewMethod.make(
                sb.toString(), helperClass
        );
        // 添加方法
        helperClass.addMethod(cm);
        // 调试文件
        helperClass.writeFile("E:/");
        // 获取 Java 类
        Class<?> javaClazz = helperClass.toClass();

        // 创建帮助对象实例
        // /////////////////////////////////////////////////////////////////////
        // 创建帮助实例
        entityHelper = (AbstractEntityHelper) javaClazz.newInstance();
        // 添加到字典
        _entityHelperMap.put(entityClazz, entityHelper);

        return entityHelper;



    }

}
